package com.eksframework.commons.json;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.TimeZone;

/**
 * 基于Jackson的json与对象之间转换的对象类
 *
 * @author: chenfenghai
 * @version: 1.0
 * @date: 2020-05-09 11:15
 * @blame Team
 */
public class EksJackson {

    private ObjectMapper mapper;

    EksJackson() {
        this.mapper = new ObjectMapper();
    }

    EksJackson(ObjectMapper objectMapper) {
        this.mapper = objectMapper;
    }

    public ObjectMapper getMapper() {
        return mapper;
    }

    public ObjectNode createObjectNode() {
        return mapper.createObjectNode();
    }

    public ArrayNode createArrayNode() {
        return mapper.createArrayNode();
    }

    /**
     * Method that allows overriding of the underlying {@link DeserializationConfig}
     * object.
     * It is added as a fallback method that may be used if no other configuration
     * modifier method works: it should not be used if there are alternatives,
     * and its use is generally discouraged.
     *<p>
     * <b>NOTE</b>: only use this method if you know what you are doing -- it allows
     * by-passing some of checks applied to other configuration methods.
     * Also keep in mind that as with all configuration of {@link ObjectMapper},
     * this is only thread-safe if done before calling any deserialization methods.
     *
     * @see ObjectMapper#setConfig(DeserializationConfig)
     */
    public EksJackson setConfig(DeserializationConfig config) {
        mapper.setConfig(config);
        return this;
    }

    /**
     * Method that allows overriding of the underlying {@link SerializationConfig}
     * object, which contains serialization-specific configuration settings.
     * It is added as a fallback method that may be used if no other configuration
     * modifier method works: it should not be used if there are alternatives,
     * and its use is generally discouraged.
     *<p>
     * <b>NOTE</b>: only use this method if you know what you are doing -- it allows
     * by-passing some of checks applied to other configuration methods.
     * Also keep in mind that as with all configuration of {@link ObjectMapper},
     * this is only thread-safe if done before calling any serialization methods.
     *
     * @see ObjectMapper#setConfig(SerializationConfig)
     */
    public EksJackson setConfig(SerializationConfig config) {
        mapper.setConfig(config);
        return this;
    }

    /**
     * Convenience method, equivalent to calling:
     *<pre>
     *  setPropertyInclusion(JsonInclude.Value.construct(incl, incl));
     *</pre>
     *<p>
     * NOTE: behavior differs slightly from 2.8, where second argument was
     * implied to be <code>JsonInclude.Include.ALWAYS</code>.
     */
    public EksJackson setSerializationInclusion(JsonInclude.Include include) {
        mapper.setSerializationInclusion(include);
        return this;
    }


    public EksJackson setDateFormat(DateFormat dateFormat) {
        mapper.setDateFormat(dateFormat);
        return this;
    }

    public EksJackson setDateFormat(String pattern) {
        return setDateFormat(new SimpleDateFormat(pattern));
    }

    public EksJackson setDateFormatDefault() {
        return setDateFormat("yyyy-MM-dd HH:mm:ss");
    }

    public EksJackson setTimeZone(TimeZone tz) {
        mapper.setTimeZone(tz);
        return this;
    }

    public EksJackson setTimeZone(String id) {
        return setTimeZone(TimeZone.getTimeZone(id));
    }

    public EksJackson setTimeZoneDefault() {
        return setTimeZone("GMT+:08:00");
    }

    public EksJackson disable(MapperFeature... f) {
        mapper.disable(f);
        return this;
    }

    public EksJackson disable(JsonGenerator.Feature... features) {
        mapper.disable(features);
        return this;
    }

    public EksJackson disable(JsonParser.Feature... features) {
        mapper.disable(features);
        return this;
    }

    public EksJackson disable(SerializationFeature f) {
        mapper.disable(f);
        return this;
    }

    public EksJackson disable(SerializationFeature first, SerializationFeature... f) {
        mapper.disable(first, f);
        return this;
    }

    public EksJackson disable(DeserializationFeature feature) {
        mapper.disable(feature);
        return this;
    }

    public EksJackson disable(DeserializationFeature first, DeserializationFeature... f) {
        mapper.disable(first, f);
        return this;
    }

    public EksJackson enable(MapperFeature... f) {
        mapper.enable(f);
        return this;
    }

    public EksJackson enable(JsonGenerator.Feature... features) {
        mapper.enable(features);
        return this;
    }

    public EksJackson enable(JsonParser.Feature... features) {
        mapper.enable(features);
        return this;
    }

    public EksJackson enable(SerializationFeature f) {
        mapper.enable(f);
        return this;
    }

    public EksJackson enable(SerializationFeature first, SerializationFeature... f) {
        mapper.enable(first, f);
        return this;
    }

    public EksJackson enable(DeserializationFeature feature) {
        mapper.enable(feature);
        return this;
    }

    public EksJackson enable(DeserializationFeature first, DeserializationFeature... f) {
        mapper.enable(first, f);
        return this;
    }

    /**
     * JSON序列化对象，Object可以是POJO，也可以是Collection或数组
     *
     * @param object Object
     * @return String json string
     * @throws IllegalArgumentException if serialization failed
     * @see EksJson#toJson(ObjectMapper, Object)
     */
    @Nonnull
    public String toJson(@Nullable Object object) {
        return EksJson.toJson(mapper, object);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为JsonNode对象
     *
     * @param jsonString JSON字符串
     * @return JsonNode对象
     * @throws IllegalArgumentException if deserialization failed
     * @see EksJson#parseObject(ObjectMapper, String)
     */
    @Nonnull
    public JsonNode parseObject(@Nullable String jsonString) {
        return EksJson.parseObject(mapper, jsonString);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param jsonNode jsonNode数据
     * @param clazz    要反序列化的对象类
     * @param <T>      类
     * @return 反序列化对象
     * @throws NullPointerException     if the jsonNode or the clazz is null
     * @throws IllegalArgumentException if deserialization failed
     * @see EksJson#parseObject(ObjectMapper, JsonNode, Class)
     */
    @Nullable
    public <T> T parseObject(@Nullable JsonNode jsonNode, @Nullable Class<T> clazz) {
        return EksJson.parseObject(mapper, jsonNode, clazz);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param jsonString 要反序列化的Json字符串
     * @param clazz      要反序列化的对象类
     * @param <T>        类
     * @return 转换后的对象
     * @throws NullPointerException     if the jsonString or the clazz is null
     * @throws IllegalArgumentException if deserialization failed
     * @see #parseObject(String, Class, DateFormat)
     */
    @Nullable
    public <T> T parseObject(@Nullable String jsonString, @Nullable Class<T> clazz) {
        return parseObject(jsonString, clazz, null);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param jsonString 要反序列化的Json字符串
     * @param clazz      要反序列化的对象类
     * @param df         日期格式
     * @param <T>        类
     * @return 转换后的对象
     * @throws IllegalArgumentException if deserialization failed
     * @see EksJson#parseObject(ObjectMapper, String, Class, DateFormat)
     */
    @Nullable
    public <T> T parseObject(@Nullable String jsonString, @Nullable Class<T> clazz, @Nullable DateFormat df) {
        return EksJson.parseObject(mapper.copy(), jsonString, clazz, df);
    }

    /**
     * JSON反序列化，把JSON数组字符串反序列化为对象列表
     *
     * @param jsonArray 要反序列化的Json字符串
     * @param clazz     要反序列化的对象类
     * @param <T>       类
     * @return List
     * @throws IllegalArgumentException if deserialization failed
     * @see EksJson#parseArray(ObjectMapper, String, Class)
     */
    @Nonnull
    public <T> List<T> parseArray(@Nullable String jsonArray, @Nullable Class<T> clazz) {
        return EksJson.parseArray(mapper, jsonArray, clazz);
    }
}
